/*
 *
 *  D-Bus++ - C++ bindings for D-Bus
 *
 *  Copyright (C) 2005-2007  Paolo Durante <shackan@gmail.com>
 *
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */


#ifndef __DBUSXX_UTIL_H
#define __DBUSXX_UTIL_H

#ifdef HAVE_CONFIG_H
#include <dbus-c++/config.h>
#endif

#include "api.h"
#include "debug.h"

namespace DBus {

/*
 *   Very simple reference counting
 */

class DXXAPI RefCnt
{
public:

	RefCnt()
	{
		__ref = new int;
		(*__ref) = 1;
	}

	RefCnt( const RefCnt& rc )
	{
		__ref = rc.__ref;
		ref();
	}

	virtual ~RefCnt()
	{
		unref();
	}

	RefCnt& operator = ( const RefCnt& ref )
	{
		ref.ref();
		unref();
		__ref = ref.__ref;
		return *this;
	}

	bool noref() const
	{
		return (*__ref) == 0;
	}

	bool one() const
	{
		return (*__ref) == 1;
	}

private:

	DXXAPILOCAL void ref() const
	{
		++ (*__ref);
	}
	DXXAPILOCAL void unref() const
	{
		-- (*__ref);

		if( (*__ref) < 0 )
		{
			debug_log("%p: refcount dropped below zero!", __ref);
		}

		if( noref() )
		{
			delete __ref;
		}
	}

private:

	int *__ref;
};

/*
 *   Reference counting pointers (emulate boost::shared_ptr)
 */

template <class T>
class RefPtrI		// RefPtr to incomplete type
{
public:

	RefPtrI( T* ptr = 0 );

	~RefPtrI();

	RefPtrI& operator = ( const RefPtrI& ref )
	{
		if( this != &ref )
		{
			if(__cnt.one()) delete __ptr;

			__ptr = ref.__ptr;
			__cnt = ref.__cnt;
		}
		return *this;
	}

	T& operator *() const
	{
		return *__ptr;
	}

	T* operator ->() const
	{
		if(__cnt.noref()) return 0;
		
		return __ptr;
	}

	T* get() const
	{
		if(__cnt.noref()) return 0;
		
		return __ptr;
	}

private:

	T*     __ptr;
	RefCnt __cnt;
};

template <class T>
class RefPtr
{
public:

	RefPtr( T* ptr = 0)
	: __ptr(ptr)
	{}

	~RefPtr()
	{
		if(__cnt.one()) delete __ptr;
	}

	RefPtr& operator = ( const RefPtr& ref )
	{
		if( this != &ref )
		{
			if(__cnt.one()) delete __ptr;

			__ptr = ref.__ptr;
			__cnt = ref.__cnt;
		}
		return *this;
	}

	T& operator *() const
	{
		return *__ptr;
	}

	T* operator ->() const
	{
		if(__cnt.noref()) return 0;
		
		return __ptr;
	}

	T* get() const
	{
		if(__cnt.noref()) return 0;
		
		return __ptr;
	}

private:

	T*     __ptr;
	RefCnt __cnt;
};

/*
 *   Typed callback template
 */

template <class R, class P>
class Callback_Base
{
public:

	virtual R call( P param ) const = 0;

	virtual ~Callback_Base()
	{}
};

template <class R, class P>
class Slot
{
public:

	Slot& operator = ( Callback_Base<R,P>* s )
	{
		_cb = s;

		return *this;
	}

	R operator()( P param ) const
	{
		/*if(_cb.get())*/ return _cb->call(param);
	}

	R call( P param ) const
	{
		/*if(_cb.get())*/ return _cb->call(param);
	}

	bool empty()
	{
		return _cb.get() == 0;
	}

private:

	RefPtr< Callback_Base<R,P> > _cb;
};

template <class C, class R, class P>
class Callback : public Callback_Base<R,P>
{
public:

	typedef R (C::*M)(P);

	Callback( C* c, M m )
	: _c(c), _m(m)
	{}

	R call( P param ) const
	{
		/*if(_c)*/ return (_c->*_m)(param);
	}

private:

	C* _c; M _m;
};

} /* namespace DBus */

#endif//__DBUSXX_UTIL_H
